package com.cy.metrics;

import com.codahale.metrics.*;
import com.cy.util.U;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.CacheStats;
import com.google.common.cache.LoadingCache;
import org.testng.annotations.Test;

import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import java.util.concurrent.*;

/**
 * https://metrics.dropwizard.io/3.1.0/
 *
 * @author yue.cao
 * @since 12-29-2020
 */
public class GaugeDemo {

	BlockingDeque<Long> queue = new LinkedBlockingDeque<>(1000);

	@Test(description = "SimpleGauge 简单的返回一个 value 比如这个例子就是 获取当前队列内元素具体数量的例子")
	public void testSimpleGauge() throws InterruptedException {
		MetricRegistry registry = new MetricRegistry();

		registry.register(MetricRegistry.name(GaugeDemo.class, "queue-size"), new Gauge<Integer>() {
			@Override
			public Integer getValue() {
				return queue.size();
			}
		});

		ConsoleReporter reporter = ConsoleReporter
				.forRegistry(registry)
				.convertRatesTo(TimeUnit.SECONDS)
				.convertDurationsTo(TimeUnit.SECONDS)
				.build();

		reporter.start(1, TimeUnit.SECONDS);

		new Thread(new Runnable() {
			@Override
			public void run() {

				while (true) {
					U.sleepRandom();
					queue.add(System.currentTimeMillis());
				}
			}
		}).start();
		new Thread(() -> {
			while (true) {
				U.sleepRandom();
				queue.poll();
			}
		}).start();
		TimeUnit.SECONDS.sleep(60);
	}

	@Test(description = "JMX Gauges 这个...JMX 貌似需要一些知识补充")
	public void testJMXGauges() throws MalformedObjectNameException, InterruptedException {
		MetricRegistry registry = new MetricRegistry();

		ConsoleReporter reporter = ConsoleReporter
				.forRegistry(registry)
				.convertRatesTo(TimeUnit.SECONDS)
				.convertDurationsTo(TimeUnit.SECONDS)
				.build();

		registry.register(MetricRegistry.name(GaugeDemo.class, "HeapMemoryUsage"),
				new JmxAttributeGauge(new ObjectName("java.lang:type=Memory"), "HeapMemoryUsage"));

		registry.register(MetricRegistry.name(GaugeDemo.class, "NonHeapMemoryUsage"),
				new JmxAttributeGauge(new ObjectName("java.lang:type=Memory"), "NonHeapMemoryUsage"));

		reporter.start(10, TimeUnit.SECONDS);

		Thread.currentThread().join();
	}


	@Test(description = "RatioGauge 返回的是一个比例 比如这个例子就是一个显示计算成功率的例子")
	public void testRatioGauge() {
		MetricRegistry registry = new MetricRegistry();

		ConsoleReporter reporter = ConsoleReporter
				.forRegistry(registry)
				.convertRatesTo(TimeUnit.SECONDS)
				.convertDurationsTo(TimeUnit.SECONDS)
				.build();

		reporter.start(1, TimeUnit.SECONDS);

		Counter totalMeter = new Counter();
		Counter successMeter = new Counter();

		registry.gauge("success-rate", () -> new RatioGauge() {
			@Override
			protected Ratio getRatio() {
				return Ratio.of(successMeter.getCount(), totalMeter.getCount());
			}
		});

		registry.gauge("success-rate-非lambda", new MetricRegistry.MetricSupplier<Gauge>() {
			@Override
			public Gauge newMetric() {
				return new RatioGauge() {
					@Override
					protected Ratio getRatio() {
						return Ratio.of(successMeter.getCount(), totalMeter.getCount());
					}
				};
			}
		});


		while (true) {
			U.sleepRandom();
			totalMeter.inc();
			try {
				int a = 1 / ThreadLocalRandom.current().nextInt(3);
				successMeter.inc();
			} catch (Exception e) {
				System.out.println("error");
			}
		}
	}


	@Test(description = "CacheGauge 如果你查询的指标消耗资源比较大 而且又不太需要实时的更新 那么就可以使用这个")
	public void testCacheGauge() throws InterruptedException {
		MetricRegistry registry = new MetricRegistry();
		ConsoleReporter reporter = ConsoleReporter
				.forRegistry(registry)
				.convertRatesTo(TimeUnit.SECONDS)
				.convertDurationsTo(TimeUnit.SECONDS)
				.build();

		reporter.start(5, TimeUnit.SECONDS);

		registry.gauge("cache-db-size", () -> new CachedGauge<Long>(10, TimeUnit.SECONDS) {

			@Override
			protected Long loadValue() {
				return System.currentTimeMillis();
			}
		});

		TimeUnit.SECONDS.sleep(20);
	}


	@Test(description = "DerivativeGauge 派生Gauge 比如我已经有一个gauge了那么我希望从中间已有gauge中抽取数据变成另一个gauge")
	public void testDerivativeGauge() {
		LoadingCache<String, String> cache = CacheBuilder.newBuilder()
				.maximumSize(5)
				.recordStats()
				.build(new CacheLoader<String, String>() {
					@Override
					public String load(String key) throws Exception {
						return key.toUpperCase();
					}
				});

		MetricRegistry registry = new MetricRegistry();
		ConsoleReporter reporter = ConsoleReporter
				.forRegistry(registry)
				.convertRatesTo(TimeUnit.SECONDS)
				.convertDurationsTo(TimeUnit.SECONDS)
				.build();

		reporter.start(5, TimeUnit.SECONDS);

		Gauge<CacheStats> cacheStatsGauge = registry.register("cache-status", new Gauge<CacheStats>() {
			@Override
			public CacheStats getValue() {
				return cache.stats();
			}
		});

		registry.register("cache-status-totalLoadTime", new DerivativeGauge<CacheStats, Long>(cacheStatsGauge) {
			@Override
			protected Long transform(CacheStats value) {
				return value.totalLoadTime();
			}
		});

		while (true) {
			cache.getUnchecked("cy");
			U.sleepRandom();
		}

	}


}
